package model.camera;

import model.ThreadHandler;
import android.hardware.Camera.Size;
import android.os.AsyncTask;
import android.os.Message;
import android.util.Log;

import com.VanDoren.ArgusV2.R;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.qrcode.QRCodeMultiReader;

public class Decoder extends AsyncTask<byte[], Void, Result[]> 
{
	private static final String TAG = Decoder.class.getSimpleName();
	
	//Fields
	private final QRCodeMultiReader _multiFormatReader = new QRCodeMultiReader();
	
	private ThreadHandler _handler;
	
	private Size _previewSize;
		
	//Constructor
	public Decoder(Size previewSize, ThreadHandler handler, byte[]... bytes) 
	{
		_previewSize = previewSize;
		_handler = handler;
	}

	/**
	 * Starts when Execute is called
	 * <p>Calls onPostExecute() when done</p>
	 */
	@Override
	protected Result[] doInBackground(byte[]... bytes) 
	{
		Thread.currentThread().setName(String.format("AsyncTask: %s", TAG));
		
		return checkBinaryBitmapForQRCodes(bytes);
	}

	/**
	 * Attempts to decode QR-codes currently present in frame.
	 * @param bytes The frame to decode
	 * @return A list of decoded data.
	 */
	private Result[] checkBinaryBitmapForQRCodes(byte[]...bytes)
	{
		Result[] results = null;
		
		final PlanarYUVLuminanceSource source = buildLuminanceSource(bytes[0]);
		
		if(source != null)
		{
			//Zxing library requires the use of a BinaryBitmap for decoding
			BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
			try 
			{
				results = _multiFormatReader.decodeMultiple(bitmap);
				if(results.length == 0)
					return null;
			} catch (ReaderException e) 
			{
				Log.d(TAG, String.format("No code found %s", e.toString()));
				return null;
			}
			finally {
				bitmap = null;
				_multiFormatReader.reset();
			}
		}
		
		return results;
	}
	
	private synchronized PlanarYUVLuminanceSource buildLuminanceSource(byte[] bytes)
	{
		return new PlanarYUVLuminanceSource(bytes, _previewSize.width, _previewSize.height, 0, 0, _previewSize.width, _previewSize.height, false);
	}

	@Override
	protected void onCancelled() 
	{
		Log.e(TAG, "CANCELLED");
	}
	
	//End of Thread Methods
	@Override
	protected void onPostExecute(Result[] results) 
	{
		if(hasResult(results))
		{
			Log.d(TAG, String.format("1 or more results found: %s", results.toString()));
			sendMessage(R.id.decodeSuccess, results);
		}
		else
		{
			Log.d(TAG, String.format("No results found"));
			sendMessage(R.id.decodeFailed, null);
		}
		
		CameraManager.addCallbackBuffer();
	}
	
	private boolean hasResult(Result[] results)
	{
		return results != null && results[0] != null;
	}	
	
	//Send message to KapstokKlasse
	private void sendMessage(int what, Object obj)
	{
		Message message = Message.obtain(_handler, what);
		
		if(obj != null)
			message.obj = obj;
		
		message.sendToTarget();
	}
}
